%{
/***
 * Pascal2C
 * Programa Lex para reconhecer um conjunto de marcas comuns em linguagens de programação.
 * Cada marca encontrada será exibida na tela precedida por um contador (geral para  todas
 * as marcas) e sucedida pelo tipo de marca.
 * As possibilidades são:
 * - Palavras reservadas,
 * - Identificadores,
 * - Operadores,
 * - Números inteiros (com ou sem sinal),
 * - Números reais (com ou sem sinal).
 */

#include <stdio.h>
#include <ctype.h>
#include <string.h>

unsigned long CntMarcas = 0;
%}

Letra                  [a-zA-Z]
Digito                 [0-9]
Cadeia                 {Letra}({Letra}|{Digito})*
NNatural               ("-"|"+")?{Digito}+
NReal                  {NNatural}("."{NNatural}+)?
OperadorTipos			  ":"
OperadorLogico			  "!"
OperadorComparacao     (=|(<(>|=)?)|(>(=)?))
OperadorAritmetico     ["+"|\-|"*"|"/"]
Atribuicao             ":="
AbreParentese          "("
FechaParentese         ")"
AbreComentario			  "{"
AspasSimples           "'"
Terminador             ;
EspacoBranco           [ ]
NovaLinha              \n*
Separador				  [,]
FimPrograma				  \.

%%

\n                        { printf ( "" ); }
{Atribuicao}              { fprintf ( yyout, "\n%05d | %20s | %30s", ++CntMarcas, yytext, "Operador de Atribuicao" ); }
{OperadorLogico}			  { fprintf ( yyout, "\n%05d | %20s | %30s", ++CntMarcas, yytext, "Operador Logico" ); }
{Cadeia}        { unsigned short i;
                 char LexemaGenerico [ 100 ];

                 for ( i = 0; i < strlen ( yytext ); i++ )
                   LexemaGenerico [ i ] = tolower ( yytext [ i ] );
                 LexemaGenerico [ i ] = '\0';
							//Definicao de palavras reservadas
                 if (
                      strcmp ( LexemaGenerico, "if"    ) == 0 ||
                      strcmp ( LexemaGenerico, "then"  ) == 0 ||
                      strcmp ( LexemaGenerico, "else"  ) == 0 ||
                      strcmp ( LexemaGenerico, "for"   ) == 0 ||
                      strcmp ( LexemaGenerico, "while" ) == 0
                    )
                   fprintf ( yyout, "\n%05d | %20s | %30s", ++CntMarcas, yytext, "Palavra Reservada" );

                     // Inicio e fim de bloco de codigo
					  else if ( strcmp( LexemaGenerico, "begin" ) == 0 )
							 fprintf ( yyout, "\n%05d | %20s | %30s", ++CntMarcas, yytext, "Inicio Bloco" );
					  else if ( strcmp ( LexemaGenerico, "end" ) == 0  )
							 fprintf ( yyout, "\n%05d | %20s | %30s", ++CntMarcas, yytext, "Fim Bloco" );

							//Operador aritmetico
					  else if ( strcmp ( LexemaGenerico, "mod" ) == 0  )
							 fprintf ( yyout, "\n%05d | %20s | %30s", ++CntMarcas, yytext, "Operador Aritmetico" );

							//Inicio do programa
                 else if ( strcmp ( LexemaGenerico, "program" ) == 0  )
							 fprintf ( yyout, "\n%05d | %20s | %30s", ++CntMarcas, yytext, "Programa" );

							//Definicao de Bibliotecas (USES)
      			  else if ( strcmp ( LexemaGenerico, "uses" ) == 0  )
							 fprintf ( yyout, "\n%05d | %20s | %30s", ++CntMarcas, yytext, "Bibliotecas" );

							//Definição de Variaveis
					  else if ( strcmp ( LexemaGenerico, "var" ) == 0  )
							 fprintf ( yyout, "\n%05d | %20s | %30s", ++CntMarcas, yytext, "Variaveis" );

						  //Operadores Logicos
                 else if (  
									 strcmp ( LexemaGenerico, "and" ) == 0 || 
                            strcmp ( LexemaGenerico, "or"  ) == 0  ||
                            strcmp ( LexemaGenerico, "not" ) == 0
								 )
                      fprintf ( yyout, "\n%05d | %20s | %30s", ++CntMarcas, yytext, "Operador Logico" );
                 else if (  
									 strcmp ( LexemaGenerico, "real" ) == 0 || 
                            strcmp ( LexemaGenerico, "integer"  ) == 0  ||
                            strcmp ( LexemaGenerico, "string" ) == 0 ||
                            strcmp ( LexemaGenerico, "char" ) == 0
								 )
							 fprintf ( yyout, "\n%05d | %20s | %30s", ++CntMarcas, yytext, "Tipo" );

						  //Identificadores
                 else
                      fprintf ( yyout, "\n%05d | %20s | %30s", ++CntMarcas, yytext, "Identificador" );
                }
{AbreComentario} {
							fprintf ( yyout, "\n%05d | %20s | %30s",   ++CntMarcas, yytext, "Abre comentario" );
							char c;
							for ( ; ; ){
								while ( (c = input()) != '}' && c != EOF ) ; //Enquanto nao fechar comentario, nao faz nada
                        if ( c == '}'){  //Encontrado o final do comentario
									fprintf ( yyout, "\n%05d | %20c | %30s",   ++CntMarcas, '}', "Fecha comentario" );
									break ; 
								}
								if (c == EOF) break;
							}
						} 
{NNatural}                { fprintf ( yyout, "\n%05d | %20s | %30s",   ++CntMarcas, yytext, "Numero Inteiro" ); }
{Terminador}              { fprintf ( yyout, "\n%05d | %20s | %30s",   ++CntMarcas, yytext, "Terminador" ); }
{NReal}                   { fprintf ( yyout, "\n%05d | %20s | %30s",   ++CntMarcas, yytext, "Numero Real"); }
{AbreParentese}           { fprintf ( yyout, "\n%05d | %20s | %30s",   ++CntMarcas, yytext, "AbreParenteses"); }
{FechaParentese}          { fprintf ( yyout, "\n%05d | %20s | %30s",   ++CntMarcas, yytext, "FechaParenteses"); }
{OperadorComparacao}      { fprintf ( yyout, "\n%05d | %20s | %30s",   ++CntMarcas, yytext, "Operador Comparacao"); }
{OperadorAritmetico}      { fprintf ( yyout, "\n%05d | %20s | %30s",   ++CntMarcas, yytext, "Operador Aritmetico"); }
{AspasSimples}				  { fprintf ( yyout, "\n%05d | %20s | %30s",   ++CntMarcas, yytext, "Aspas Simples"); }
{FimPrograma}             { fprintf ( yyout, "\n%05d | %20s | %30s",   ++CntMarcas, yytext, "Fim Programa"); }
{NovaLinha}               {;}
{EspacoBranco}            {;}
{OperadorTipos}           { fprintf ( yyout, "\n%05d | %20s | %30s",   ++CntMarcas, yytext, "Definição Tipos"); }
{Separador}               { fprintf ( yyout, "\n%05d | %20s | %30s",   ++CntMarcas, yytext, "Separador"); }
.                         { fprintf ( yyout, "\n%05d | %20s | %30s",   ++CntMarcas, yytext, "Lexema Invalido"); }

%%

main (int argc, char *argv[])
{
 printf ( "\nEste é um programa Lex para reconhecer um conjunto reduzido de marcas presentes\nna linguagem Pascal." );

 if ( argc == 1 ) {
   printf ( "\n\nAlternativamente, você pode invocá-lo num modo não interativo, conforme segue:" );
   printf ( "\nSintaxe: %s <Arquivo-de-Entrada> <Arquivo-de-Saída>", argv[0] );
   printf ( "\n\nDigite comandos completos do Pascal e, ao final, pressione Enter." );
   printf ( "\nPara finalizar, pressione Ctrl+C...\n" );
 }

 if ( argc >= 2 )
   if ( ( yyin = fopen ( argv[1], "r" ) ) == NULL ) {
     printf ( "\n\nNão foi possível abrir arquivo de entrada: \"%s\".\nUtilizando entrada padrão: stdin.", argv[1] );
     printf ( "\n\nDigite comandos completos do Pascal e, ao final, pressione Enter." );
     printf ( "\nPara finalizar, pressione Ctrl+C...\n" );
     yyin = stdin;
   }

 if ( argc == 3 )
   if ( ( yyout = fopen ( argv[2], "w" ) ) == NULL ) {
     printf ( "\nNão foi possível abrir arquivo de saída: %s.\nUtilizando saída padrão: stdout.\n\n", argv[2] );
     yyout = stdout;
   }

 printf ( "\n------+----------------------+-------------------------------" );
 printf ( "\n Seq. |  Lexema  Encontrado  | Token" );
 printf ( "\n------+----------------------+-------------------------------" );
 yylex();
 printf ( "\n------+----------------------+-------------------------------\n\n" );

 return ( 0 );
}